package com.alibaba.sdk.exception;

import com.alibaba.sdk.constant.ApplicationErrorConstant;
import com.alibaba.sdk.enums.ErrorCodeEnums;
import com.alibaba.sdk.model.OperationResult;
import com.alibaba.sdk.util.ConfigPropertiesReader;
import com.alibaba.sdk.util.HttpRequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.List;

/**
 * 常见的异常: 自定义异常, 缺少参数异常, spring参数校验异常, 参数绑定异常, 使用RequestBody接收参数json转换异常,
 * 流文件下载异常, HTTP Method异常
 *
 * @ writer Shawn(Shawn_0217@163.com)
 * @ date & time 2021/4/26 22:46
 * @ company alibaba
 */
@Slf4j
@Order(-1)
@ControllerAdvice(annotations = {Controller.class, RestController.class})
public class BaseExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public OperationResult exceptionHandler(Exception e) {
        String errorStack = getErrorStack(e);
        log.error(errorStack);
        String originMsg = e.getMessage();
        String tempMsg = ConfigPropertiesReader.getPropertyValue(e.getMessage());
        if (StringUtils.isNotBlank(tempMsg)) {
            return new OperationResult(originMsg, tempMsg);
        } else {
            String errorMsg = ConfigPropertiesReader.getPropertyValue(ApplicationErrorConstant.SYSTEM_ERROR_CODE);
            return new OperationResult(ApplicationErrorConstant.SYSTEM_ERROR_CODE, errorMsg);
        }
    }

    /**
     * 参数校验框架
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public OperationResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        return new OperationResult(ApplicationErrorConstant.SYSTEM_ERROR_CODE, getValidMsg(errors, e));
    }

    /**
     * 参数校验框架: @NotBlank
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public OperationResult bindExceptionHandler(BindException e) {
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        return new OperationResult(ApplicationErrorConstant.SYSTEM_ERROR_CODE, getValidMsg(errors, e));
    }

    /**
     * 获取校验框架错误信息
     *
     * @param errors
     * @return
     */
    private String getValidMsg(List<ObjectError> errors, Exception e) {
        StringBuffer errorMsg = new StringBuffer();
        for (ObjectError error : errors) {
            errorMsg.append(error.getDefaultMessage());
            errorMsg.append("|");
        }
        String errorStack = getErrorStack(e);
        log.error(errorStack);
        return errorMsg.substring(0, errorMsg.length() - 1);
    }

    /**
     * 获取异常详细信息
     *
     * @param e
     * @return
     */
    private static String getErrorStack(Exception e) {
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(bo);
        e.printStackTrace(ps);
        String errorStack = new String(bo.toByteArray(), Charset.defaultCharset());
        return errorStack;
    }

    /**
     * 捕捉shiro的异常 用户未登录
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(ShiroException.class)
    @ResponseBody
    public OperationResult handle404(ShiroException e) {
        log.warn(ApplicationErrorConstant.USER_NO_LOGIN, e);
        return OperationResult.failWithCode(ApplicationErrorConstant.USER_NO_LOGIN);
    }

    /**
     * 捕捉UnauthorizedException  当前用户没有权限
     * @return
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(UnauthorizedException.class)
    @ResponseBody
    public OperationResult handle401(UnauthorizedException e) {
        log.warn(ApplicationErrorConstant.HAS_NO_AUTHORIZATION, e);
        return OperationResult.failWithCode(ApplicationErrorConstant.HAS_NO_AUTHORIZATION);
    }

    /**
     * 枚举转换异常处理
     *
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    public OperationResult handleEnumConvertException(HttpServletRequest request, IllegalArgumentException e) {

        String reqUri = HttpRequestUtil.delPathVariable(request);
        log.info("reqUri = {}", reqUri);
        log.warn("IllegalArgumentException:", e.getMessage());
        return new OperationResult(ErrorCodeEnums.ENUM_CONVERT_ERROR.getErrorCode(), ErrorCodeEnums.ENUM_CONVERT_ERROR.getErrorMsg());
    }
}
